home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 007 / csr20a.arc / CHECKC.C next >
Text File  |  1986-12-12  |  13KB  |  397 lines

  1. /* Christmas, 1985 - Bob Pritchett */
  2. /*   Modify and distribute as you will, just leave the credits. */
  3. /*   ...My small gift to the PD world... */
  4.  
  5. /* NOTE: This source code is NO LONGER in the Public Domain.  I have */
  6. /* left the above message and the other comments to give a history of */
  7. /* the program.  It is currently a part of the C Spot Run C Add-On Library. */
  8.  
  9. /* Tiny bug and multiple files added the day after... */
  10.  
  11. /* 01/09/1986 RDP Fixed a bug, (yes, another...) and added error tally. */
  12. /* 02/07/1986 RDP Going for effiecient analysis... */
  13. /* 02/08/1986 RDP More analysis... */
  14. /* 06/15/1986 RDP More work on 2.0.... */
  15. /* 07/16/1986 RDP Continuing on 2.0, hopefully near completion. */
  16. /*                Version 2.1 now.  CheckC.ANL eliminated. */
  17. /* 07/17/1986 RDP More accurate location of some errors. */
  18. /* 07/19/1986 RDP Can trace a new error with the ; check. */
  19. /* 08/05/1986 RDP Attempting to fix ; error.... */
  20. /* 09/16/1986 RDP Oh, what the heck.  Let's add comment beginning variable. */
  21. /*                (This tells what line starts 'EOF with comment open.') */
  22.  
  23. /*
  24.  
  25.  
  26.         Simply put, this program will check your C code's syntax in just
  27.         about every concievable way that does not involve CheckC to track
  28.         variable names or any other identifiers.  If you really care, read
  29.         CHECKC.DOC.  Informal testing has shown that it can narrow down
  30.         almost any syntactical error to within 3 lines, and it supports full
  31.         error recovery.  (When I say syntax errors, I mean {} () [] "" ''
  32.         comments, etc.)  The program follows K&R for syntax, so those of you
  33.         with compilers that allow nested comments, watch out.  (They are
  34.         illegal in K&R, and thus here.)
  35.  
  36.         This program has grown from a simple 'curly brace checker' into 
  37.         what I feel is a powerful and useful tool for C programmers.  I
  38.         hope you agree, and that it proves to be a useful tool for you.
  39.  
  40.           Bob Pritchett
  41.           New Dimension Software
  42.             23 Pawtucket Dr.
  43.             Cherry Hill, NJ 08003
  44.             Voice: (609) 424-2595
  45.              Data: (609) 354-9259  (300B/1200B)  (24hrs.)
  46.  
  47. */
  48.  
  49. #include <stdio.h> 
  50. #include <ctype.h>    /* For the isalpha() macro */
  51.  
  52. FILE *data;        /* Input File */
  53.  
  54. int in_comment;        /* Flag - Positive if in Comments */
  55. int in_single;        /* Flag - Positive if in Single Quotes */
  56. int in_double;        /* Flag - Positive if in Double Quotes */
  57.  
  58. int paren;        /* Temporary Paren Count */
  59. int tparen;        /* Total (Per File) Paren Count */
  60.  
  61. int brack;        /* Bracket Count */
  62.  
  63. int square;        /* Temporary Square Bracket Count */
  64. int tsquare;        /* Total (Per File) Square Bracket Count */
  65.  
  66. int errors;        /* Error Count (Per File) */
  67.  
  68. int line;        /* Current Line Number */
  69.  
  70. int anal;        /* Analysis Flag - Positive if Analysis On */
  71.  
  72. int tbrack;        /* Extra Bracket Count */
  73.  
  74. int semi;        /* Semicolon Counter for for Statements */
  75.  
  76. int cbl;        /* Line on Which Last Comment Began */
  77.  
  78. main(argc,argv)
  79.  int argc;
  80.  char *argv[];
  81.  {
  82.   int x = 1;        /* First Argument that is a File - 1 is Default */
  83.   int c;        /* Current Character */
  84.   int lastc;        /* Last Character */
  85.   int llast;        /* Character Before lastc */
  86.   int vlast;        /* Character Before llast */
  87.   int forflag;        /* Flag to mark for statement */
  88.   int err_flg = 0;    /* Total errors in all files. */
  89.   printf("\n  CheckC Version 2.1   Copyright 1985, 86 Bob Pritchett\n");
  90.   printf("                 A small C code checker.\n");
  91.   if ( argc < 2 )    /* if no arguments */
  92.    {
  93.     printf("\nCHECKC: Usage is CHECKC [/A] <filename> [<filename>...]\n");
  94.     exit(1);        /* Exit with failure flag */
  95.    }
  96.   if ( argv[1][0] == '/' && ( argv[1][1] == 'A' || argv[1][1] == 'a' ) )
  97.    {
  98.     x = 2;        /* First Argument is an option */
  99.     anal = 1;        /* Analysis on */
  100.    }
  101.   for ( ; x < argc; ++x )    /* For Each File... */
  102.    {
  103.     in_comment = 0;    /* Initialize Data */
  104.     in_single = 0;
  105.     in_double = 0;
  106.     paren = 0;
  107.     tparen = 0;
  108.     brack = 0;
  109.     square = 0;
  110.     tsquare = 0;
  111.     errors = 0;
  112.     line = 1;
  113.     tbrack = 0;
  114.     semi = 0;
  115.     forflag = 0;
  116.     cbl = 0;
  117.     c = 0;
  118.     lastc = 0;
  119.     llast = 0;
  120.     vlast = 0;
  121.  
  122.     strcpy(argv[x],strupr(argv[x]));    /* Put file name in upper case */
  123.     if ( ( data = fopen(argv[x],"r") ) == 00 )    /* If unable to open source */
  124.      {
  125.       printf("\nCHECKC: Error Opening %s\n",argv[x]);    /* Print message */
  126.       continue;                /* and continue with next file */
  127.      }
  128.     if ( anal )
  129.        printf("\n        Checking and Analyzing %s\n\n",argv[x]);
  130.     else
  131.        printf("\n        Checking %s\n\n",argv[x]);
  132.     while ( c != EOF )
  133.      {
  134.       vlast = llast;
  135.       llast = lastc;
  136.       lastc = c;
  137.       c = getc(data);    /* Get character from data file */
  138.  
  139.       if ( c == '\n' )    /* Increment here so comment lines are counted. */
  140.        {
  141.         if ( in_single )
  142.          {
  143.           printf("Line %04d:  Missing single quote.  Diagnostics now inaccurate.\n",line);
  144.           in_single = 0;            /* Fix flag for diags. */
  145.           ++errors;
  146.          }
  147.         else if ( in_double )
  148.          {
  149.           printf("Line %04d:  Missing double quote.  Diagnostics now inaccurate.\n",line);
  150.           in_double = 0;            /* Fix flag for diags. */
  151.           ++errors;
  152.          }
  153.         ++line;
  154.         continue;
  155.        }
  156.  
  157.       if ( in_double )        /* If in double quotes, check for end quote */
  158.        {                                           /* It is an ending " if  */
  159.         if ( ( c == '"' && lastc == '\\' && llast == '\\' ) ||  /*  \\"  or */
  160.              ( c == '"' && lastc != '\\' ) )                    /*  !  \"   */ 
  161.            in_double = 0;    /* If so, turn off quote flag */
  162.         continue;        /* and continue with next character */
  163.        }
  164.  
  165.       if ( in_comment )        /* If in a Comment */
  166.        {
  167.         if ( c == '/' && lastc == '*' )    /* Check for end comment sign */
  168.            in_comment = 0;        /* If comment ends, change flag */
  169.         continue;            /* and get next character */
  170.        }
  171.  
  172.       if ( in_single )        /* If in single quotes */
  173.        {            /* check for closing single quote */
  174.         if ( ( c == 39 && lastc == '\\' && llast == '\\' ) ||   /* 39 is ' */
  175.              ( c == 39 && lastc != '\\' ) )
  176.            in_single = 0;                 
  177.         continue;                         
  178.        }
  179.  
  180.       switch ( c )    /* If not in a comment or quote */
  181.        {        /* check and count all relevant characters */
  182.         case '(':
  183.           ++paren;
  184.           ++tparen;
  185.           break;
  186.         case ')':
  187.           --paren;
  188.           --tparen;
  189.           break;
  190.         case '{':
  191.           ++brack;
  192.           ++tbrack;
  193.           break;
  194.         case '}':
  195.           --brack;
  196.           --tbrack;
  197.           break;
  198.         case '[':
  199.           ++square;
  200.           ++tsquare;
  201.           break;
  202.         case ']':
  203.           --square;
  204.           --tsquare;
  205.           break;
  206.         case '"':
  207.           in_double = 1;
  208.           break;
  209.         case 39:
  210.           in_single = 1;
  211.           break;
  212.         case '*':    /* Must be last so break w/o correct if is ok. */
  213.           if ( lastc == '/' )
  214.            {
  215.             in_comment = 1;
  216.             cbl = line;    /* Latest comment started here. */
  217.            }
  218.           break;
  219.         default:
  220.           break;
  221.        }
  222.  
  223.       if ( anal )    /* If in analysis mode, attempt to diagnose */
  224.        {        /* problem by the level of brackets etc. and */
  225.         if ( c == '{' )    /* call appropriate error. */
  226.          {
  227.           if ( brack == 1 )
  228.            {
  229.             if ( paren > 0 )
  230.                error(1);
  231.             if ( square > 0 )
  232.                error(7);
  233.            } 
  234.           else if ( brack > 1 )
  235.            {
  236.             if ( paren > 0 )
  237.                error(3);
  238.             if ( square > 0 )
  239.                error(9);
  240.            }
  241.          }
  242.         else if ( c == '}' )
  243.          {
  244.           if ( brack == 0 )
  245.            {
  246.             if ( paren > 0 )
  247.                error(2);
  248.             if ( square > 0 )
  249.                error(8);
  250.            }
  251.           else if ( brack > 0 )
  252.            {
  253.             if ( paren > 0 )
  254.                error(4);
  255.             if ( square > 0 )
  256.                error(10);
  257.            } 
  258.           else
  259.              error(13);
  260.          }
  261.  
  262.         if ( c == '(' && forflag == 1 )
  263.          {
  264.           semi = 1;        /* Must wait till for statement is begun */
  265.           forflag = 0;        /* before allowing ; counting in statement */
  266.          }
  267.  
  268.         if ( square < 0 )    /* Never allowed to go under */
  269.            error(11);
  270.         else if ( square > 0 && c == ';' )  /* Not even in for statement */
  271.            error(12);
  272.  
  273.         if ( c == 'r' && lastc == 'o' && llast == 'f' )
  274.          {
  275.           if ( isalpha(vlast) || vlast == '_' )    /* If it's an identifier */
  276.              break;        /* ..stop here */
  277.           c = getc(data);    /* Peek at next character */
  278.           if ( ! isalpha(c) && c != '_' )      /* If it's not an identifier */
  279.              forflag = 1;    /* ...it's a for statment */
  280.           ungetc(c,data);    /* Put it back to be processed */
  281.           c = 'r';        /* Put the correct letter in c again */
  282.          }
  283.         else if ( c == ';' && semi > 0 )    /* If counting has started */
  284.            ++semi;        /* and there is a ;, record it */
  285.  
  286.         if ( paren < 0 )    /* Never allowed to go under */
  287.            error(5);
  288.         else if ( paren > 0 && semi == 0 && c == ';' )
  289.            error(6);
  290.         else if ( paren == 0 )
  291.          {
  292.           if ( semi > 3 )
  293.              error(14);
  294.           else if ( semi == 1 || semi == 2 )
  295.              error(15);
  296.           semi = 0;
  297.          }
  298.  
  299.        }        /* Close of Analysis Stage */
  300.  
  301.      }            /* Close of While Loop */
  302.  
  303.     putchar('\n');
  304.  
  305.     fclose(data);    /* Close source file */
  306.  
  307.     if ( tparen > 0 )
  308.        printf("CHECKC: %d too many ('s.\n",tparen);
  309.     else if ( tparen < 0 )
  310.        printf("CHECKC: %d too many )'s.\n",abs(tparen));
  311.  
  312.     if ( tbrack > 0 )
  313.        printf("CHECKC: %d extra }'s or missing {'s.\n",tbrack);
  314.     else if ( tbrack < 0 )
  315.        printf("CHECKC: %d too many {'s.\n",abs(tbrack));
  316.  
  317.     if ( tsquare > 0 )
  318.        printf("CHECKC: %d too many ['s.\n",tsquare);
  319.     else if ( tsquare < 0 )
  320.        printf("CHECKC: %d too many ]'s.\n",abs(tsquare));
  321.  
  322.     if ( in_double == 1 )
  323.        printf("CHECKC: EOF with double quote open.\n");
  324.  
  325.     if ( in_comment == 1 )
  326.        printf("CHECKC: EOF with comment open.  Comment opened on line %04d.\n"
  327.               ,cbl);
  328.  
  329.     if ( in_single == 1 )
  330.        printf("CHECKC: EOF with single quote open.\n");
  331.  
  332.     errors += in_single + in_comment + in_double +     /* Total all errors */
  333.               abs( tsquare ) +
  334.               abs( brack ) +
  335.               abs( tparen ); 
  336.  
  337.     err_flg += errors;        /* Flag positive if any files have errors. */
  338.  
  339.     printf("\n        %d Errors found in %s\n",errors,argv[x]);
  340.  
  341.     if ( anal )
  342.        printf("        Check and Analysis of %s Completed\n",argv[x]);
  343.     else 
  344.        printf("        Check of %s Completed\n",argv[x]);
  345.  
  346.    }
  347.   if ( err_flg != 0 )    /* If errors found in any file */
  348.      exit(1);        /* Exit with 1 in ERRORLEVEL. */
  349.   exit(0);        /* Otherwise exit cleanly. */
  350.  }
  351.  
  352. /* ----------------------------------------------------------- */
  353.  
  354. error(x)
  355.  int x;
  356.  {
  357.   printf("Line %04d:  ",line);    /* Line on which error first noticed */
  358.   if ( x == 1 )
  359.      printf("New function started with %d ('s open.",paren);
  360.   else if ( x == 7 )
  361.      printf("New function started with %d ['s open.",square);
  362.   else if ( x == 2 )
  363.      printf("Function ended with %d ('s open.",paren);
  364.   else if ( x == 8 )
  365.      printf("Function ended with %d ['s open.",square);
  366.   else if ( x == 3 )
  367.      printf("Block started with %d ('s open.",paren);
  368.   else if ( x == 9 )
  369.      printf("Block started with %d ['s open.",square);
  370.   else if ( x == 4 )
  371.      printf("Block ended with %d ('s open.",paren);
  372.   else if ( x == 10 )
  373.      printf("Block ended with %d ['s open.",square);
  374.   else if ( x == 5 )
  375.      printf("%d extra )'s or missing ('s.",abs(paren));
  376.   else if ( x == 6 )
  377.      printf("Semicolon with %d extra ('s or missing )'s.",paren);
  378.   else if ( x == 11 )
  379.      printf("%d extra ]'s or missing ['s.",abs(square));
  380.   else if ( x == 12 )
  381.      printf("Semicolon with %d extra ['s or missing ]'s.",square);
  382.   else if ( x == 13 )    /* A bad { } count can ruin the analysis accuracy */
  383.    {
  384.     printf("An extra } or missing {.");
  385.     brack = 0;
  386.    }
  387.   else if ( x == 14 )
  388.      printf("A for statement with %d extra ;'s.",semi-2);
  389.   else if ( x == 15 )
  390.      printf("A for statement without two ;'s.");
  391.   if ( x > 0 && x < 7 )
  392.      paren = 0;
  393.   else if ( x > 6 && x < 13 )
  394.      square = 0;
  395.   putchar('\n');
  396.  }
  397.